﻿using System;
using System.ComponentModel.Composition;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using Microsoft.SilverlightMediaFramework.Plugins.Metadata;
using Microsoft.SilverlightMediaFramework.Plugins.Primitives;

namespace Microsoft.SilverlightMediaFramework.Plugins.DualMedia
{
    [ExportMediaPlugin(PluginName = PluginName,
        PluginDescription = PluginDescription,
        PluginVersion = PluginVersion,
        SupportedDeliveryMethods = SupportedDeliveryMethodsInternal)]
    [PartCreationPolicy(CreationPolicy.NonShared)]
    public class DualMediaPlugin : IMediaPlugin
    {
        private const string PluginName = "DualMediaPlugin";
        private const string PluginDescription = "Provides 3D capabilities for the Silverlight Media Framework using multiple MediaPlugins.";
        private const string PluginVersion = "2.2011.0410.1";
        private const DeliveryMethods SupportedDeliveryMethodsInternal = DeliveryMethods.NotSpecified;

        public event Func<Uri, Uri> GetLeftSource;
        public event Func<Uri, Uri> GetRightSource;

        protected Canvas canvas;

        void canvas_SizeChanged(object sender, SizeChangedEventArgs e)
        {
            SizeChanged(e.NewSize);
        }

        protected void SizeChanged(Size NewSize)
        {
            if (leftSource != null && rightSource != null)
            {
                if (Stretch != Stretch.None)
                {
                    var width = NewSize.Width / 2;
                    Canvas.SetTop(leftSource.VisualElement, 0);
                    Canvas.SetTop(rightSource.VisualElement, 0);
                    Canvas.SetLeft(leftSource.VisualElement, 0);
                    Canvas.SetLeft(rightSource.VisualElement, width);
                    leftSource.VisualElement.Width = width;
                    rightSource.VisualElement.Width = width;
                    leftSource.VisualElement.Height = NewSize.Height;
                    rightSource.VisualElement.Height = NewSize.Height;
                }
                else
                {
                    var width = leftSource.NaturalVideoSize.Width;
                    Canvas.SetTop(leftSource.VisualElement, 0);
                    Canvas.SetTop(rightSource.VisualElement, 0);
                    Canvas.SetLeft(leftSource.VisualElement, 0);
                    Canvas.SetLeft(rightSource.VisualElement, width);
                    leftSource.VisualElement.Width = width;
                    leftSource.VisualElement.Height = leftSource.NaturalVideoSize.Height;
                    rightSource.VisualElement.Width = rightSource.NaturalVideoSize.Width;
                    rightSource.VisualElement.Height = rightSource.NaturalVideoSize.Height;
                }
            }
        }

        bool autoPlay;
        public bool AutoPlay
        {
            get { return autoPlay; }
            set
            {
                autoPlay = value;
                if (leftSource != null) leftSource.AutoPlay = value;
                if (rightSource != null) rightSource.AutoPlay = value;
            }
        }

        public CacheMode CacheMode
        {
            get
            {
                return leftSource.CacheMode;
            }
            set
            {
                leftSource.CacheMode = value;
                rightSource.CacheMode = value;
            }
        }

        public double Balance
        {
            get { return leftSource.Balance; }
            set
            {
                leftSource.Balance = value;
                rightSource.Balance = value;
            }
        }

        public double BufferingProgress
        {
            get { return leftSource.BufferingProgress; }
        }

        TimeSpan bufferingTime;
        public TimeSpan BufferingTime
        {
            get { return bufferingTime; }
            set
            {
                bufferingTime = value;
                if (leftSource != null) leftSource.BufferingTime = value;
                if (rightSource != null) rightSource.BufferingTime = value;
            }
        }

        public bool CanPause
        {
            get { return leftSource.CanPause; }
        }

        public bool CanSeek
        {
            get { return leftSource.CanSeek; }
        }

        public Microsoft.SilverlightMediaFramework.Plugins.Primitives.MediaPluginState CurrentState
        {
            get
            {
                return leftSource.CurrentState;
            }
        }

        public double DownloadProgress
        {
            get { return leftSource.DownloadProgress; }
        }

        public TimeSpan Duration
        {
            get { return leftSource.Duration; }
        }

        public TimeSpan EndPosition
        {
            get { return leftSource.EndPosition; }
        }

        public bool IsMuted
        {
            get { return leftSource.IsMuted; }
            set
            {
                leftSource.IsMuted = value;
                // right source always stays muted to prevent both sources from playing sound simultaneously
                //rightSource.IsMuted = value;
            }
        }

        public LicenseAcquirer LicenseAcquirer
        {
            get { return leftSource.LicenseAcquirer; }
            set
            {
                leftSource.LicenseAcquirer = value;
                rightSource.LicenseAcquirer = value;
            }
        }

        public double PlaybackRate
        {
            get { return leftSource.PlaybackRate; }
            set
            {
                leftSource.PlaybackRate = value;
                rightSource.PlaybackRate = value;
            }
        }

        public TimeSpan Position
        {
            get { return leftSource.Position; }
            set
            {
                leftSource.Position = value;
                rightSource.Position = value;
            }
        }

        public TimeSpan StartPosition
        {
            get { return leftSource.StartPosition; }
        }

        public Microsoft.SilverlightMediaFramework.Plugins.Primitives.DeliveryMethods SupportedDeliveryMethods
        {
            get { return leftSource.SupportedDeliveryMethods; }
        }

        public System.Collections.Generic.IEnumerable<double> SupportedPlaybackRates
        {
            get { return leftSource.SupportedPlaybackRates; }
        }

        double volume;
        public double Volume
        {
            get { return volume; }
            set
            {
                volume = value;
                if (leftSource != null) leftSource.Volume = value;
                // don't let the right source play sound in order to prevent double sound.
                //if (rightSource != null) rightSource.Volume = value;
            }
        }

        public Uri Source
        {
            get { return leftSource.Source; }
            set
            {
                leftSource.Source = (GetLeftSource != null) ? GetLeftSource(value) : value;
                rightSource.Source = (GetRightSource != null) ? GetRightSource(value) : value;
            }
        }

        public System.IO.Stream StreamSource
        {
            get { return leftSource.StreamSource; }
            set
            {
                //leftSource.StreamSource = value;
                //rightSource.StreamSource = value;
            }
        }

        public Size NaturalVideoSize
        {
            get
            {
                //return leftSource.NaturalVideoSize;
                return new Size(leftSource.NaturalVideoSize.Width * 2, leftSource.NaturalVideoSize.Height);
            }
        }

        Stretch stretch = Stretch.Uniform;
        public Stretch Stretch
        {
            get
            {
                return stretch;
            }
            set
            {
                stretch = value;

                if (leftSource != null) leftSource.Stretch = value;
                if (rightSource != null) rightSource.Stretch = value;

                if (stretch != System.Windows.Media.Stretch.None)
                {
                    //canvas.Width = double.NaN;
                    //canvas.Height = double.NaN;
                    //canvas.HorizontalAlignment = HorizontalAlignment.Stretch;
                    //canvas.VerticalAlignment = VerticalAlignment.Stretch;
                    //canvas.UpdateLayout();
                    SizeChanged(new Size(canvas.ActualWidth, canvas.ActualHeight));
                }
                else
                {
                    SizeChanged(new Size(canvas.ActualWidth, canvas.ActualHeight));
                }
            }
        }

        public bool EnableGPUAcceleration
        {
            get
            {
                return leftSource.EnableGPUAcceleration;
            }
            set
            {
                leftSource.EnableGPUAcceleration = value;
                rightSource.EnableGPUAcceleration = value;
            }
        }

        public FrameworkElement VisualElement
        {
            get { return canvas; }
        }

        public double DroppedFramesPerSecond
        {
            get
            {
                return leftSource.DroppedFramesPerSecond;
            }
        }

        public double RenderedFramesPerSecond
        {
            get
            {
                return leftSource.RenderedFramesPerSecond;
            }
        }

        public bool SupportsAdScheduling
        {
            get { return leftSource.SupportsAdScheduling; }
        }

        public event Action<IMediaPlugin, double> BufferingProgressChanged;

        public event Action<IMediaPlugin, double> DownloadProgressChanged;

        public event Action<IMediaPlugin, Microsoft.SilverlightMediaFramework.Plugins.Primitives.MediaMarker> MarkerReached;

        public event Action<IMediaPlugin> MediaEnded;

        public event Action<IMediaPlugin, Exception> MediaFailed;

        public event Action<IMediaPlugin> MediaOpened;

        public event Action<IMediaPlugin> PlaybackRateChanged;

        public event Action<IMediaPlugin, Microsoft.SilverlightMediaFramework.Plugins.Primitives.MediaPluginState> CurrentStateChanged;

        public event Action<IMediaPlugin> SeekCompleted;

        public event Action<IAdaptiveMediaPlugin, Microsoft.SilverlightMediaFramework.Plugins.Primitives.IAdContext> AdClickThrough;

        public event Action<IAdaptiveMediaPlugin, Microsoft.SilverlightMediaFramework.Plugins.Primitives.IAdContext> AdError;

        public event Action<IAdaptiveMediaPlugin, Microsoft.SilverlightMediaFramework.Plugins.Primitives.IAdContext, Microsoft.SilverlightMediaFramework.Plugins.Primitives.AdProgress> AdProgressUpdated;

        public void Play()
        {
            RightSource.Play();
            LeftSource.Play();
        }

        public void Pause()
        {
            RightSource.Pause();
            LeftSource.Pause();
        }

        public void Stop()
        {
            rightSource.Stop();
            leftSource.Stop();
        }

        public void RequestLog()
        {
            leftSource.RequestLog();
            rightSource.RequestLog();
        }

        public Microsoft.SilverlightMediaFramework.Plugins.Primitives.IAdContext ScheduleAd(Uri adSource, Microsoft.SilverlightMediaFramework.Plugins.Primitives.DeliveryMethods deliveryMethod, TimeSpan? duration = null, TimeSpan? startTime = null, TimeSpan? startOffset = null, Uri clickThrough = null, bool pauseTimeline = true, Microsoft.SilverlightMediaFramework.Plugins.Primitives.IAdContext appendToAd = null, object data = null)
        {
            rightSource.ScheduleAd(adSource, deliveryMethod, duration, startTime, startOffset, clickThrough, pauseTimeline, appendToAd, data);
            return leftSource.ScheduleAd(adSource, deliveryMethod, duration, startTime, startOffset, clickThrough, pauseTimeline, appendToAd, data);
        }

        public bool IsLoaded
        {
            get { return leftSource != null ? leftSource.IsLoaded : false; }
        }

        public event Action<IPlugin, Microsoft.SilverlightMediaFramework.Plugins.Primitives.LogEntry> LogReady;

        public event Action<IPlugin> PluginLoaded;

        public event Action<IPlugin> PluginUnloaded;

        public event Action<IPlugin, Exception> PluginLoadFailed;

        public event Action<IPlugin, Exception> PluginUnloadFailed;

        public virtual void Load()
        {
            try
            {
                canvas = new Canvas() { HorizontalAlignment = HorizontalAlignment.Stretch, VerticalAlignment = VerticalAlignment.Stretch };
                canvas.SizeChanged += new SizeChangedEventHandler(canvas_SizeChanged);

                if (rightSource != null)
                {
                    rightSource.Load();
                    rightSource.IsMuted = true; // force the right source to be muted so we don't hear double sound.
                    canvas.Children.Add(rightSource.VisualElement);
                }

                if (leftSource != null)
                {
                    AttachEvents();
                    leftSource.Load();
                    canvas.Children.Add(leftSource.VisualElement);
                }
            }
            catch (Exception ex)
            {
                if (PluginLoadFailed != null) PluginLoadFailed(this, ex);
            }
        }

        void TrickleProperties(IMediaPlugin source)
        {
            source.Volume = volume;
            source.BufferingTime = bufferingTime;
            source.Stretch = stretch;
            source.AutoPlay = autoPlay;
        }

        protected virtual void AttachEvents()
        {
            leftSource.PluginLoaded += new Action<IPlugin>(leftSource_PluginLoaded);
            leftSource.PluginLoadFailed += new Action<IPlugin, Exception>(leftSource_PluginLoadFailed);
            leftSource.PluginUnloaded += new Action<IPlugin>(leftSource_PluginUnloaded);
            leftSource.PluginUnloadFailed += new Action<IPlugin, Exception>(leftSource_PluginUnloadFailed);

            leftSource.BufferingProgressChanged += new Action<IMediaPlugin, double>(leftSource_BufferingProgressChanged);
            leftSource.DownloadProgressChanged += new Action<IMediaPlugin, double>(leftSource_DownloadProgressChanged);
            leftSource.MarkerReached += new Action<IMediaPlugin, MediaMarker>(leftSource_MarkerReached);
            leftSource.MediaEnded += new Action<IMediaPlugin>(leftSource_MediaEnded);
            leftSource.MediaFailed += new Action<IMediaPlugin, Exception>(leftSource_MediaFailed);
            leftSource.MediaOpened += new Action<IMediaPlugin>(leftSource_MediaOpened);
            leftSource.PlaybackRateChanged += new Action<IMediaPlugin>(leftSource_PlaybackRateChanged);
            leftSource.CurrentStateChanged += new Action<IMediaPlugin, MediaPluginState>(leftSource_CurrentStateChanged);
            leftSource.SeekCompleted += new Action<IMediaPlugin>(leftSource_SeekCompleted);
            leftSource.AdClickThrough += new Action<IAdaptiveMediaPlugin, IAdContext>(leftSource_AdClickThrough);
            leftSource.AdError += new Action<IAdaptiveMediaPlugin, IAdContext>(leftSource_AdError);
            leftSource.AdProgressUpdated += new Action<IAdaptiveMediaPlugin, IAdContext, AdProgress>(leftSource_AdProgressUpdated);
        }

        protected virtual void DetachEvents()
        {
            leftSource.PluginLoaded -= new Action<IPlugin>(leftSource_PluginLoaded);
            leftSource.PluginLoadFailed -= new Action<IPlugin, Exception>(leftSource_PluginLoadFailed);
            leftSource.PluginUnloaded -= new Action<IPlugin>(leftSource_PluginUnloaded);
            leftSource.PluginUnloadFailed -= new Action<IPlugin, Exception>(leftSource_PluginUnloadFailed);

            leftSource.BufferingProgressChanged -= new Action<IMediaPlugin, double>(leftSource_BufferingProgressChanged);
            leftSource.DownloadProgressChanged -= new Action<IMediaPlugin, double>(leftSource_DownloadProgressChanged);
            leftSource.MarkerReached -= new Action<IMediaPlugin, MediaMarker>(leftSource_MarkerReached);
            leftSource.MediaEnded -= new Action<IMediaPlugin>(leftSource_MediaEnded);
            leftSource.MediaFailed -= new Action<IMediaPlugin, Exception>(leftSource_MediaFailed);
            leftSource.MediaOpened -= new Action<IMediaPlugin>(leftSource_MediaOpened);
            leftSource.PlaybackRateChanged -= new Action<IMediaPlugin>(leftSource_PlaybackRateChanged);
            leftSource.CurrentStateChanged -= new Action<IMediaPlugin, MediaPluginState>(leftSource_CurrentStateChanged);
            leftSource.SeekCompleted -= new Action<IMediaPlugin>(leftSource_SeekCompleted);
            leftSource.AdClickThrough -= new Action<IAdaptiveMediaPlugin, IAdContext>(leftSource_AdClickThrough);
            leftSource.AdError -= new Action<IAdaptiveMediaPlugin, IAdContext>(leftSource_AdError);
            leftSource.AdProgressUpdated -= new Action<IAdaptiveMediaPlugin, IAdContext, AdProgress>(leftSource_AdProgressUpdated);
        }

        void leftSource_AdProgressUpdated(IAdaptiveMediaPlugin arg1, IAdContext arg2, AdProgress arg3)
        {
            if (AdProgressUpdated != null) AdProgressUpdated(arg1, arg2, arg3);
        }

        void leftSource_AdError(IAdaptiveMediaPlugin arg1, IAdContext arg2)
        {
            if (AdError != null) AdError(arg1, arg2);
        }

        void leftSource_AdClickThrough(IAdaptiveMediaPlugin arg1, IAdContext arg2)
        {
            if (AdClickThrough != null) AdClickThrough(arg1, arg2);
        }

        void leftSource_SeekCompleted(IMediaPlugin obj)
        {
            if (SeekCompleted != null) SeekCompleted(this);
        }

        void leftSource_CurrentStateChanged(IMediaPlugin arg1, MediaPluginState arg2)
        {
            if (CurrentStateChanged != null) CurrentStateChanged(this, arg2);
        }

        void leftSource_PlaybackRateChanged(IMediaPlugin obj)
        {
            if (PlaybackRateChanged != null) PlaybackRateChanged(this);
        }

        void leftSource_MediaOpened(IMediaPlugin obj)
        {
            if (MediaOpened != null) MediaOpened(this);
        }

        void leftSource_MediaFailed(IMediaPlugin arg1, Exception arg2)
        {
            if (MediaFailed != null) MediaFailed(this, arg2);
        }

        void leftSource_MediaEnded(IMediaPlugin obj)
        {
            if (MediaEnded != null) MediaEnded(this);
        }

        void leftSource_MarkerReached(IMediaPlugin arg1, MediaMarker arg2)
        {
            if (MarkerReached != null) MarkerReached(this, arg2);
        }

        void leftSource_DownloadProgressChanged(IMediaPlugin arg1, double arg2)
        {
            if (DownloadProgressChanged != null) DownloadProgressChanged(this, arg2);
        }

        void leftSource_BufferingProgressChanged(IMediaPlugin arg1, double arg2)
        {
            if (BufferingProgressChanged != null) BufferingProgressChanged(this, arg2);
        }

        void leftSource_PluginUnloadFailed(IPlugin arg1, Exception arg2)
        {
            if (PluginUnloadFailed != null) PluginUnloadFailed(this, arg2);
        }

        void leftSource_PluginUnloaded(IPlugin obj)
        {
            if (PluginUnloaded != null) PluginUnloaded(this);
        }

        void leftSource_PluginLoadFailed(IPlugin arg1, Exception arg2)
        {
            if (PluginLoadFailed != null) PluginLoadFailed(this, arg2);
        }

        void leftSource_PluginLoaded(IPlugin obj)
        {
            if (PluginLoaded != null) PluginLoaded(this);
        }

        public virtual void Unload()
        {
            if (canvas != null)
            {
                canvas.SizeChanged -= new SizeChangedEventHandler(canvas_SizeChanged);
                canvas.Children.Clear();
                canvas = null;
            }

            if (rightSource != null) rightSource.Unload();
            if (leftSource != null)
            {
                leftSource.Unload();
                DetachEvents();
            }
        }

        IMediaPlugin leftSource;
        public IMediaPlugin LeftSource
        {
            get { return leftSource; }
            set
            {
                leftSource = value;

                if (IsLoaded)
                    throw new Exception("Source must be set before load. Recommended to set in RegisterMediaPlugin event");
            }
        }

        IMediaPlugin rightSource;
        public IMediaPlugin RightSource
        {
            get { return rightSource; }
            set
            {
                rightSource = value;

                if (IsLoaded)
                    throw new Exception("Source must be set before load. Recommended to set in RegisterMediaPlugin event");
            }
        }

    }
}
